package com.zero.guava.samples.Cache;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * https://juejin.im/post/5d3561bf518825165444687f
 */
public class CacheBuilderTest {

    public static void main(String[] args) throws InterruptedException {
        //testFirst();
        //testSecond();
        //testExpireAfterWrite();
        //testExpireAfterAccess();
        //testInvalidateAll();
        testRemovalListener();
    }

    /**
     * 构建简单Cache
     */
    public static void testFirst()  {
        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder().build();
        cache.put("a",new TradeAccount("q","abc",3.5));
        cache.put("b",new TradeAccount("q1","abc1",3.5));
        cache.put("c",new TradeAccount("q2","abc2",3.5));
        cache.put("d",new TradeAccount("q3","abc3",3.5));
        cache.put("e",new TradeAccount("q4","abc4",3.5));

        System.out.println(cache.getIfPresent("c"));
    }


    /**
     * 设置最大存储(maximumSize)
     * Guava Cache可以在构建缓存对象时指定缓存所能够存储的最大记录数量.
     * 当Cache中的记录数量达到最大值后再调用put方法向其中添加对象,
     * Guava会先从当前缓存的对象记录中选择一条删除掉，腾出空间后再将新的对象存储到Cache中。
     */
    public static void testSecond(){

        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .build();

        cache.put("a",new TradeAccount("q","abc",3.5));
        cache.put("b",new TradeAccount("q1","abc1",3.5));
        cache.put("c",new TradeAccount("q2","abc2",3.5));

        System.out.println("第一个值：" + cache.getIfPresent("a"));
        System.out.println("第二个值：" + cache.getIfPresent("b"));
        System.out.println("第三个值：" + cache.getIfPresent("c"));
    }

    /**
     * 设置过期时间
     *
     * 在构建Cache对象时,可以通过CacheBuilder类的expireAfterAccess和expireAfterWrite两个方法为缓存中的对象指定过期时间,
     * 过期的对象将会被缓存自动删除。
     * expireAfterWrite方法指定对象被写入到缓存后多久过期;
     * expireAfterAccess指定对象多久没有被访问后过期
     */
    public static void testExpireAfterWrite() throws InterruptedException {

        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterWrite(2, TimeUnit.SECONDS)
                .build();

        cache.put("a",new TradeAccount("q","abc",3.5));
        cache.put("b",new TradeAccount("q2","abc",3.5));

        int time = 1;
        while(true) {
            System.out.println("第" + time++ + "次取到a的值为：" + cache.getIfPresent("a"));
            System.out.println("第" + time++ + "次取到b的值为：" + cache.getIfPresent("b"));
            Thread.sleep(1000);
        }

    }

    /**
     * 通过CacheBuilder的expireAfterAccess方法指定Cache中存储的对象如果超过3秒没有被访问就会过期.
     * while中的代码每sleep一段时间就会访问一次Cache中存储的对象key1,每次访问key1之后下次sleep的时间会加长一秒.
     * 程序运行结果如下图所示,从结果中可以看出,当超过3秒没有读取key1对象之后,该对象会自动被Cache删除.
     */
    public static void testExpireAfterAccess() throws InterruptedException {

        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder()
                .maximumSize(2)
                .expireAfterAccess(2, TimeUnit.SECONDS)
                .build();

        cache.put("a",new TradeAccount("q","abc",3.5));
        cache.put("b",new TradeAccount("q2","abc",3.5));

        int time = 1;
        while(true) {
            Thread.sleep(time*1000);
            System.out.println("睡眠" + time++ + "秒后取到A的值为：" + cache.getIfPresent("a"));
            System.out.println("睡眠" + time++ + "秒后取到B的值为：" + cache.getIfPresent("b"));
        }
    }

    /**
     * 调用Cache的invalidateAll或invalidate方法显示删除Cache中的记录。
     * invalidate方法一次只能删除Cache中一个记录,接收的参数是要删除记录的key.
     * invalidateAll方法可以批量删除Cache中的记录,当没有传任何参数时,invalidateAll方法将清除Cache中的全部记录.
     * invalidateAll也可以接收一个Iterable类型的参数，参数中包含要删除记录的所有key值。
     */
    public static void testInvalidateAll(){
        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder().build();

        cache.put("a",new TradeAccount("q","abc",3.5));
        cache.put("b",new TradeAccount("q2","abc",3.5));

        List<String> list = Lists.newArrayList();
        list.add("a");
        list.add("b");

        cache.invalidateAll(list);

        System.out.println(cache.getIfPresent("a"));
        System.out.println(cache.getIfPresent("b"));
    }

    /**
     * 有记录被删除时可以感知到这个事件
     */
    public static void testRemovalListener() throws InterruptedException {

        RemovalListener<String, TradeAccount> listener = new RemovalListener<String, TradeAccount>() {
            @Override
            public void onRemoval(RemovalNotification<String, TradeAccount> notification) {
                System.out.println("[" + notification.getKey() + ":" + notification.getValue() + "] is removed!");
            }
        };

        Cache<String, TradeAccount> cache = CacheBuilder.newBuilder()
                .maximumSize(3)
                .removalListener(listener)
                .expireAfterWrite(1,TimeUnit.SECONDS)
                .build();

        cache.put("a",new TradeAccount("q","abc",3.5));
        Thread.sleep(1000);
        cache.put("b",new TradeAccount("q1","abc1",3.5));
        Thread.sleep(2000);
        cache.put("c",new TradeAccount("q2","abc2",3.5));
        Thread.sleep(2000);


        System.out.println(cache.getIfPresent("a"));
        System.out.println(cache.getIfPresent("b"));
        System.out.println(cache.getIfPresent("c"));
    }

}
